require "luci.http"
require "luci.http.protocol.mime"
require "mime"
require "luci.ltn12"
require "luci.util"
require "luci.jsonc"
require "luci.sys"
require "nixio"
require "ubus"
require "uci"
require "os"

MERCKU_HTTP_ENDLINE_FLAG = "\r\n"

MERCKU_C_API_VERSION = "4"
MERCKU_SERVER_VERSION = "uhttpd-2015-11-08"
MERCKU_MIME_TYPE_JSON = "application/json; charset=utf-8"
MERCKU_MIME_TYPE_FILE = "text/html; charset=utf-8"
MERCKU_COOIKES_NAME = "session"
MERCKU_USER_NAME_ROOT = "root"
MERCKU_USER_NAME_ADMIN = "admin"
MERCKU_MAX_LOGIN_LIMIT_TIME = "60"
MERCKU_MAX_LOGIN_LIMIT_CNT = "5"
MERCKU_UPLOADFILE_NAME = "/tmp/mercku_upload.tmp"
MERCKU_FIRMWARE_NAME = "/tmp/mercku_firmware.img"
MERCKU_UPLOAD_FORM_FILE = "file"
MERCKU_UPLOAD_FORM_TYPE = "type"
MERCKU_FIRMWARE_UPLOAD_APP = "firmware_upload"
MERCKU_FILE_UPLOAD_APP = "file_upload"
MERCKU_API_HANDLE_APP = "app"
MERCKU_HOME_INDEX_APP = "/"

MERCKU_WEB_HOME_PATH="/web"
MERCKU_WEB_HOME_INDEX = "index.html"

MERCKU_HOME_REDIRECT = "/web/wlan"

MERCKU_LUA_DEBUG_LOG = "/var/log/mercku_uhttpd_lua.log"

-- 32 kB maximum content size
MERCKU_HTTP_MAX_CONTENT = 1024*32

--[[ upload file max size: 50M ]]--
MERCKU_FIRMWARE_MAX_SIZE = 52428800

local TRUE = {
    ['1'] = true,
    ['on'] = true,
    ['On'] = true,
    ['ON'] = true,
    ['t'] = true,
    ['T'] = true,
    ['true'] = true,
    ['True'] = true,
    ['TRUE'] = true,
};

local FALSE = {
    ['0'] = false,
    ['off'] = false,
    ['Off'] = false,
    ['OFF'] = false,
    ['f'] = false,
    ['F'] = false,
    ['false'] = false,
    ['False'] = false,
    ['FALSE'] = false,
};

local function mercku_toboolean(value)
    assert(type(value) == 'string', 'string expected')

    if TRUE[value] == true then
        return true;
    elseif FALSE[value] == false then
        return false;
    else
        return false, strformat('cannot convert %q to boolean', value);
    end
end

local function debug_to_file(file_name, content)
    local f = assert(io.open(file_name, 'a+'))
    if content ~= nil then
        f:write(content)
    else
        f:write("nil")
    end
    f:write("\n")
    f:close()
end

function is_guest_enabled()
    -- local x = uci.cursor()
    -- local is_guest_enabled = x:get("wireless", "guest_5g", "enabled")
    -- local is_bridge_mode = x:get("system", "global", "work_mode")
    -- return (mercku_toboolean(is_guest_enabled) and mercku_toboolean(is_bridge_mode))
    return false
end

local function uhttpd_debug(content)
    debug_to_file(MERCKU_LUA_DEBUG_LOG, content)
end

function get_cooikes()
    local timestamp = os.time()
    local cooikes = string.format("s" .. timestamp)
    return cooikes
end

function read_all_file(fname)
    local f = assert(io.open(fname, 'r'))
    local content = f:read("*all")
    f:close()
    return content
end

HTTP_STATUS_MSG = {
    [200] = "OK",
    [206] = "Partial Content",
    [301] = "Moved Permanently",
    [302] = "Found",
    [304] = "Not Modified",
    [400] = "Bad Request",
    [401] = "Unauthorized",
    [403] = "Forbidden",
    [404] = "Not Found",
    [405] = "Method Not Allowed",
    [408] = "Request Time-out",
    [411] = "Length Required",
    [412] = "Precondition Failed",
    [416] = "Requested range not satisfiable",
    [500] = "Internal Server Error",
    [503] = "Server Unavailable",
}

function http_send(status, content, cooikes, send_func)
    local statusmsg = HTTP_STATUS_MSG[status]
    if statusmsg == nil then
        statusmsg = "Unknown"
    end

    local status_str = tostring(status)
    statusmsg = " " .. statusmsg
    send_func("Status: " .. status_str .. statusmsg .. MERCKU_HTTP_ENDLINE_FLAG)

    send_func("Server: " .. MERCKU_SERVER_VERSION .. MERCKU_HTTP_ENDLINE_FLAG)
    send_func("Accept-Ranges: bytes" .. MERCKU_HTTP_ENDLINE_FLAG)
    send_func("X-Mercku-API-Version: " .. MERCKU_C_API_VERSION .. MERCKU_HTTP_ENDLINE_FLAG)
    if cooikes ~= nil then
        local set_cooikes = MERCKU_COOIKES_NAME .. "=" .. cooikes
        send_func("Set-Cookie: " .. set_cooikes .. MERCKU_HTTP_ENDLINE_FLAG)
    end

    --[[ add Cache-control ]]--
    send_func("Cache-control: no-cache" .. MERCKU_HTTP_ENDLINE_FLAG)
    send_func("Pragma: no-cache" .. MERCKU_HTTP_ENDLINE_FLAG)
    send_func("Expires: 0" .. MERCKU_HTTP_ENDLINE_FLAG)
    --[[ add cooikes ]]--

    send_func("Content-Type: " .. MERCKU_MIME_TYPE_JSON .. MERCKU_HTTP_ENDLINE_FLAG)
    if content ~= nil then
        send_func(MERCKU_HTTP_ENDLINE_FLAG)
        send_func(content)
    end
end

function http_file(status, filename, location, send_func)
    local statusmsg = HTTP_STATUS_MSG[status]
    if statusmsg == nil then
        statusmsg = "Unknown"
    end

    local status_str = tostring(status)
    statusmsg = " " .. statusmsg
    send_func("Status: " .. status_str .. statusmsg .. MERCKU_HTTP_ENDLINE_FLAG)

    send_func("Server: " .. MERCKU_SERVER_VERSION .. MERCKU_HTTP_ENDLINE_FLAG)
    send_func("Accept-Ranges: bytes" .. MERCKU_HTTP_ENDLINE_FLAG)
    send_func("X-Mercku-API-Version: " .. MERCKU_C_API_VERSION .. MERCKU_HTTP_ENDLINE_FLAG)
    if location ~= nil then
        send_func("Location: " .. location .. MERCKU_HTTP_ENDLINE_FLAG)
    end

    --[[ add Cache-control ]]--
    send_func("Cache-control: no-cache" .. MERCKU_HTTP_ENDLINE_FLAG)
    send_func("Pragma: no-cache" .. MERCKU_HTTP_ENDLINE_FLAG)
    send_func("Expires: 0" .. MERCKU_HTTP_ENDLINE_FLAG)
    --[[ add cooikes ]]--

    local content_type = luci.http.protocol.mime.to_mime(filename)
    if content_type == nil then
        content_type = MERCKU_MIME_TYPE_FILE
    end
    send_func("Content-Type: " .. content_type .. MERCKU_HTTP_ENDLINE_FLAG)

    local file_content = read_all_file(filename)
    if file_content ~= nil then
        send_func(MERCKU_HTTP_ENDLINE_FLAG)
        send_func(file_content)
    end
end

function get_web_home()
    local x = uci.cursor()
    local webhome = x:get("uhttpd", "main", "home")
    return webhome
end

function get_sys_is_init()
    local file = io.popen("mtool is_initialized");
    local output = file:read("*a")
    file:close()
    return output == "false"
    -- local x = uci.cursor()
    -- local is_init = x:get("system", "global", "is_init")
    -- return is_init
end

UCI_FILE_CONFIG_PATH = "/tmp/"
UCI_FILE_TEMP_PATH = "/tmp/.uci/"

UCI_FILE_SESSION = "mercku_sessions"
UCI_TYPE_SESSION = "user"
UCI_OPTION_SESSION = "session"

--[[
config user 'root'
        option session 's1576584224'
]]--

function get_session_id(usrname)
    if usrname == nil then return nil end
    local x = uci.cursor()
    local session = x:get(UCI_FILE_SESSION, usrname, UCI_OPTION_SESSION)
    return session
end

function get_session_name(session_id)
    if session_id == nil then return nil end

    local root_session = get_session_id(MERCKU_USER_NAME_ROOT)
    local admin_session = get_session_id(MERCKU_USER_NAME_ADMIN)

    if root_session == session_id then 
        return MERCKU_USER_NAME_ROOT 
    elseif admin_session == session_id then 
        return MERCKU_USER_NAME_ADMIN 
    end

    return nil
end

function isvalid_sessionid(session_id)
    local usrname = get_session_name(session_id)
    return usrname ~= nil
end

-- save seddion id to uci config
function create_sessionid(session_id, usrname)
    -- create uci session config
    local x = uci.cursor()
    local cfg_session = x:get(UCI_FILE_SESSION, usrname, UCI_OPTION_SESSION)
    if cfg_session == nil then
        -- if the entry[usrname] does not exist, create it
        x:set(UCI_FILE_SESSION, usrname, UCI_TYPE_SESSION)
    end
    x:set(UCI_FILE_SESSION, usrname, UCI_OPTION_SESSION, session_id)
    x:commit(UCI_FILE_SESSION)
end

function save_sessionid(usrname)
    if usrname ~= nil then
        local sessionid = get_session_id(usrname)
        if sessionid == nil then
            sessionid = get_cooikes()
            create_sessionid(sessionid, usrname)
        end
        return sessionid
    else
        return nil
    end
end

function delete_sessionid(session_id)
    --[[
    local usrname = get_session_name(session_id)
    if usrname == nil then return nil end
    local x = uci.cursor()
    x:delete(UCI_FILE_SESSION, usrname)
    x:commit(UCI_FILE_SESSION)
    ]]--
end

UCI_FILE_LOGIN_LIMIT = "mercku_login"
UCI_TYPE_LOGIN_LIMIT = "login"
UCI_NAME_LOGIN_LIMIT = "login_limit"

function save_login_limit()
    -- create the uci file if it does not exist
    local fd_login = nixio.open(UCI_FILE_CONFIG_PATH .. UCI_FILE_LOGIN_LIMIT, "a+")
    fd_login:close()
    -- create uci session config
    local x = uci.cursor(UCI_FILE_CONFIG_PATH, UCI_FILE_TEMP_PATH)
    local failures_time = os.time() -- get current unix time(seconds)
    local failures_cnt = x:get(UCI_FILE_LOGIN_LIMIT, UCI_NAME_LOGIN_LIMIT, "failures_cnt")
    if failures_cnt == nil then
        -- if the entry[login_limit] does not exist, create it
        x:set(UCI_FILE_LOGIN_LIMIT, UCI_NAME_LOGIN_LIMIT, UCI_TYPE_LOGIN_LIMIT)
        failures_cnt = "0"
    end
    failures_cnt = tostring(failures_cnt + 1) -- increase the number of failures
    x:set(UCI_FILE_LOGIN_LIMIT, UCI_NAME_LOGIN_LIMIT, "failures_cnt", failures_cnt)
    x:set(UCI_FILE_LOGIN_LIMIT, UCI_NAME_LOGIN_LIMIT, "failures_time", failures_time)
    x:commit(UCI_FILE_LOGIN_LIMIT)
end

function isvalid_login_limit()
    local login_limit_flag = true
    local curr_time = os.time()

    -- create the uci file if it does not exist
    local fd_login = nixio.open(UCI_FILE_CONFIG_PATH .. UCI_FILE_LOGIN_LIMIT, "a+")
    fd_login:close()

    local x = uci.cursor(UCI_FILE_CONFIG_PATH, UCI_FILE_TEMP_PATH)

    local failures_time = x:get(UCI_FILE_LOGIN_LIMIT, UCI_NAME_LOGIN_LIMIT, "failures_time")
    if failures_time == nil then failures_time = "0" end

    local failures_cnt = x:get(UCI_FILE_LOGIN_LIMIT, UCI_NAME_LOGIN_LIMIT, "failures_cnt")
    if failures_cnt == nil then failures_cnt = "0" end

    failures_time = tostring(curr_time - failures_time)
    if tonumber(failures_time) > tonumber(MERCKU_MAX_LOGIN_LIMIT_TIME) then
        x:set(UCI_FILE_LOGIN_LIMIT, UCI_NAME_LOGIN_LIMIT, "failures_cnt", "0")
        login_limit_flag =  false
    else
        if tonumber(failures_cnt) < tonumber(MERCKU_MAX_LOGIN_LIMIT_CNT) then
            login_limit_flag = false
        end
    end

    x:commit(UCI_FILE_LOGIN_LIMIT)

    return login_limit_flag
end

function delete_login_limit(clr_ftime)
    local x = uci.cursor(UCI_FILE_CONFIG_PATH, UCI_FILE_TEMP_PATH)

    x:set(UCI_FILE_LOGIN_LIMIT, UCI_NAME_LOGIN_LIMIT, "failures_cnt", "0")

    if clr_ftime == true then
        x:set(UCI_FILE_LOGIN_LIMIT, UCI_NAME_LOGIN_LIMIT, "failures_time", "0")
    end

    x:commit(UCI_FILE_LOGIN_LIMIT)
end

--[[
    mqtt.xxx --> mercku.mqtt
    ips.xxx --> mercku.ips
    home.xxx --> mercku.home
    mss.xxx --> mercku.mss

    mesh.xxx --> mercku.mesh
    node.xxx --> mercku.mesh
    router.xxx --> mercku.mesh
    factory.xxx --> mercku.mesh
]]--
function ubus_call(cbody)
    local mercku_ubus_obj = nil

    --[[
    if string.find(method, "ips.") ~= nil then
        mercku_ubus_obj = "mercku.ips"
    elseif string.find(method, "home.") ~= nil then
        mercku_ubus_obj = "mercku.home"
    elseif string.find(method, "mss.") ~= nil then
        mercku_ubus_obj = "mercku.mss"
    else
        mercku_ubus_obj = "mercku.mesh"
    end
    ]]--
    mercku_ubus_obj = "mercku.api"

    local ubus_jresponse = luci.util.ubus(mercku_ubus_obj, "api", cbody)
    uhttpd_debug("ubus_jresponse : " .. luci.jsonc.stringify(ubus_jresponse, false))
    local resp = luci.jsonc.parse(ubus_jresponse["payload"])
    uhttpd_debug("resp : " .. luci.jsonc.stringify(resp, false))
    for k, v in pairs(resp) do
        uhttpd_debug("k : " .. k .. ", v : " .. luci.jsonc.stringify(v))
    end
    if resp == nil then return nil end
    return luci.jsonc.stringify(resp, false)
end

function error_code(parsed_response)
    local response_error

    -- uhttpd_debug("response_json : " .. response)
    -- local parsed_response = luci.jsonc.parse(response)

    response_error = parsed_response["error"]

    if response_error == nil then
        return 0
    else
        return response_error["code"]
    end
end

MERCKU_UNPROTECTED_METHODS = {
    "router.is_initial", 
    "router.meta.get", 
    "mesh.wan.status.get",
    "router.wan.status.get", 
    "factory.get.sn_suitid", 
    "factory.write.suitid",
    "factory.start.telnetd",
    "home.meta.get",
    "home.bind"
}

function isneed_authenticate(method)
    for i, v in pairs(MERCKU_UNPROTECTED_METHODS) do
        if v == method then return false end
    end
    return true
end

function request_authenticate(req, need_authen)
    local authen_success = false
    if req == nil then return false end
    if need_authen == false then --[[don't need authenticate]] --
        authen_success = true
    else --[[need authenticate]] --
        local session = req:getcookie(MERCKU_COOIKES_NAME)
        if session ~= nil then
            authen_success = isvalid_sessionid(session) --[[authen the session]] --
        else
            authen_success = false
        end
    end
    return authen_success
end

function my_filehandler(message)
    --[[
    Solve the problem that luci cannot receive more than 8K data.
    For details, please refer to <mercku_issues/issues/5344#issuecomment-619663044>
    ]]--
    message.content = ""
    message.content_length = 0
    local function file_handler(meta, chunk, unknown)
        if chunk then
            if ( message.content_length + #chunk ) <= MERCKU_HTTP_MAX_CONTENT then
                message.content = message.content .. chunk
                message.content_length = message.content_length + #chunk
                return true
            else
                return nil, "POST data exceeds maximum allowed length"
            end
        else
            return true
        end
    end
    return file_handler
end

function my_upload_handler(image_name)
    local fp = nil
    local image_tmp = image_name
    local function file_handler(meta, chunk, eof)
        if not fp then
            fp = io.open(image_tmp, "w")
        end
        if chunk then
            local len=fp:seek("end")
            if (len < MERCKU_FIRMWARE_MAX_SIZE ) then
                fp:write(chunk)
            end
        end
        if eof then
            fp:close()
        end
    end
    return file_handler
end

function gen_error_msg(errcode, errmsg)
    local resp_err = {error = {code = errcode, message = errmsg}}
    local resp_err_str = luci.jsonc.stringify(resp_err, false)
    return resp_err_str
end

function gen_status_msg(is_true)
    local result_status = { result = { status = is_true } }
    local result_string = luci.jsonc.stringify(result_status, false)
    return result_string
end

function gen_response_msg(resp_msg)
    local result_msg = { result = resp_msg }
    local result_string = luci.jsonc.stringify(result_msg, false)
    return result_string
end

function gen_resp_msg(msg_kv)
    local result_string = luci.jsonc.stringify(msg_kv, false)
    return result_string
end

function handle_firmware_app(req, send)
    uhttpd_debug("handle_firmware_app now !!!")
    req:setfilehandler(my_upload_handler(MERCKU_FIRMWARE_NAME))
    uhttpd_debug("file write !!!" .. MERCKU_FIRMWARE_NAME)
    if true then
        --[[ http.context.request = req ]]--
        local firmware_name = req:formvalue(MERCKU_UPLOAD_FORM_FILE)
        if firmware_name ~= nil then
            -- uhttpd_debug("firmware_name : " .. firmware_name)
            --[[ send the method to merckud ubus ]]--
            local fw_check_method = "router.firmware.check"
            local fw_check_api = {method = fw_check_method}
            -- local fw_check_api_str = luci.jsonc.stringify(fw_check_api, false)
            --[[ uhttpd_debug("fw_check_api_str : " .. fw_check_api_str) ]]--
            local check_response = ubus_call(fw_check_api)
            if check_response == nil then
                http_send(400, unknown_error_resp, nil, send)
            else
                if error_code(check_response) ~= 0 then
                    http_send(400, check_response, nil, send)
                else
                    http_send(200, check_response, nil, send)
                end
            end
        else
            http_send(400, unknown_error_resp, nil, send)
        end
    else
        local authen_failure_resp = gen_error_msg(300103, "Auth failed")
        http_send(401, authen_failure_resp, nil, send)
    end
end

function handle_fileupload_app(req, send)
    req:setfilehandler(my_upload_handler(MERCKU_UPLOADFILE_NAME))
    if request_authenticate(req, true) == false then
        --[[ http.context.request = req ]]--
        local uploadfile_name = req:formvalue(MERCKU_UPLOAD_FORM_FILE)
        local uploadfile_type = req:formvalue(MERCKU_UPLOAD_FORM_TYPE)
        if (uploadfile_name == nil) or (uploadfile_type == nil) then
            http_send(400, unknown_error_resp, nil, send)
        else
            uhttpd_debug("uploadfile_name : " .. uploadfile_name)
            uhttpd_debug("uploadfile_type : " .. uploadfile_type)
            local response_value = {
                file = uploadfile_name,
                type = uploadfile_type,
                url = MERCKU_UPLOADFILE_NAME
            }
            local response_msg = gen_response_msg(response_value)
            if response_msg == nil then
                http_send(400, unknown_error_resp, nil, send)
            else
                http_send(200, response_msg, nil, send)
            end
        end
    else
        local authen_failure_resp = gen_error_msg(300103, "Auth failed")
        http_send(401, authen_failure_resp, nil, send)
    end
end

function handle_merckuapi_app(req, send)
    local unknown_error_resp = gen_error_msg(109999, "Unknown error")
    local status_true_resp = gen_status_msg(true)
    local status_false_resp = gen_status_msg(false)

    req:setfilehandler(my_filehandler(req.message))
    local req_seesion = req:getcookie(MERCKU_COOIKES_NAME)

    local cbody, clen = req:content() --[[ get http request data, json string ]]--
    if (clen > 0) and (cbody ~= nil) then
        jrequest = luci.jsonc.parse(cbody)
        if jrequest == nil then http_send(400, unknown_error_resp, nil, send) end
        if jrequest.method == "request.get" then
            local remote_ip = req:getenv("REMOTE_ADDR")
            if remote_ip == nil then 
                http_send(400, unknown_error_resp, nil, send)
            else
                local reqget_resp = { result = { ip = remote_ip } }
                http_send(200, gen_resp_msg(reqget_resp), nil, send)
            end
        else
            uhttpd_debug("session is " .. luci.jsonc.stringify(req_seesion))
            jrequest.session = req_seesion
            local is_empty_params = jrequest["params"] == nil or next(jrequest["params"]) == nil
            if is_empty_params then
                jrequest["params"] = {
                    placeholder = true,
                }
            end
            local call_response = ubus_call(jrequest)
            if call_response == nil then
                http_send(400, unknown_error_resp, nil, send)
            else
                local resp_body = luci.jsonc.parse(call_response)
                local err_code = error_code(resp_body)
                if err_code == 200101 then
                    http_send(401, call_response, nil, send)
                elseif err_code ~= 0 then
                    http_send(400, call_response, nil, send)
                else
                    http_send(200, call_response, resp_body.session, send)
                end
            end
        end
        return
    else
        local content_error_resp = gen_error_msg(109999, "Get nil POST data, Maybe exceeds maximum allowed length")
        http_send(400, content_error_resp, nil, send)
    end
end

function is_sys_initial()
    return get_sys_is_init()
    -- local is_init = get_sys_is_init()
    -- return is_init ~= "0"
end

function handle_home_index(req, send)
    local web_home_path = get_web_home() --[[ MERCKU_WEB_HOME_PATH ]]--
    local file_path = web_home_path .. MERCKU_HOME_INDEX_APP .. MERCKU_WEB_HOME_INDEX
    local scode = 200
    local location = nil
    if is_sys_initial() == true then
        scode = 302
        location = MERCKU_HOME_REDIRECT
    end
    http_file(scode, file_path, location, send)
end

function handle_home_index_nolocation(req, send)
    local web_home_path = get_web_home() --[[ MERCKU_WEB_HOME_PATH ]]--
    local file_path = web_home_path .. MERCKU_HOME_INDEX_APP .. MERCKU_WEB_HOME_INDEX
    local scode = 200
    local location = nil
    http_file(scode, file_path, location, send)
end

--[ block guest device ]--
function is_guest_sta(ip)
    local result = 0
    if is_guest_enabled() then
        result = luci.sys.call("/usr/bin/mercku_device.sh is_guest_sta " ..ip)
    end
    return result
end

function handle_request(env)
    uhttpd_debug("handle_request now !!!")

    local send = uhttpd.send

    local unknown_error_resp = gen_error_msg(109999, "Unknown error")
    local status_true_resp = gen_status_msg(true)
    local status_false_resp = gen_status_msg(false)

    local renv = {
        CONTENT_LENGTH = env.CONTENT_LENGTH,
        CONTENT_TYPE = env.CONTENT_TYPE,
        REQUEST_METHOD = env.REQUEST_METHOD,
        REQUEST_URI = env.REQUEST_URI,
        PATH_INFO = env.PATH_INFO,
        SCRIPT_NAME = env.SCRIPT_NAME:gsub("/+$", ""),
        SCRIPT_FILENAME = env.SCRIPT_NAME,
        SERVER_PROTOCOL = env.SERVER_PROTOCOL,
        QUERY_STRING = env.QUERY_STRING,
        REMOTE_ADDR = env.REMOTE_ADDR
    }

    local k, v
    for k, v in pairs(env.headers) do
        k = k:upper():gsub("%-", "_") --[[ Replace "-" with "_" ]] --
        renv["HTTP_" .. k] = v
    end

    --[[ local renv_msg = gen_response_msg(renv)
    uhttpd_debug("renv_msg : " .. renv_msg) ]]--

    local limit = tonumber(env.CONTENT_LENGTH) or 0
    local BLOCKSIZE = luci.ltn12.BLOCKSIZE

    local function my_recv()
        if limit > 0 then
            local rlen, rbuf = uhttpd.recv(BLOCKSIZE)
            --[[ uhttpd_debug("rlen: " .. rlen .. ", limit:" .. limit) ]]--
            if rlen >= 0 then
                limit = limit - rlen
                return rbuf
            end
        end
        return nil
    end
    if is_guest_sta(env.REMOTE_ADDR) == 1 then
        local authen_failure_resp = gen_error_msg(300103, "Permission denied")
        http_send(401, authen_failure_resp, nil, send)
        return nil
    end

    local req = luci.http.Request(renv, my_recv, luci.ltn12.sink.file(io.stderr))
    uhttpd_debug("request.uri: "..env.REQUEST_URI)

    --[[ uhttpd_debug("env.REQUEST_URI : " .. env.REQUEST_URI) ]]--
    if (env.REQUEST_URI ~= nil) and (string.find(env.REQUEST_URI, MERCKU_FIRMWARE_UPLOAD_APP) ~= nil) then
        handle_firmware_app(req, send)
    elseif (env.REQUEST_URI ~= nil) and (string.find(env.REQUEST_URI, MERCKU_FILE_UPLOAD_APP) ~= nil) then
        handle_fileupload_app(req, send)
    elseif (env.REQUEST_URI ~= nil) and (string.find(env.REQUEST_URI, MERCKU_API_HANDLE_APP) ~= nil) then
        handle_merckuapi_app(req, send)
    elseif (env.REQUEST_URI ~= nil) and (env.REQUEST_URI == MERCKU_HOME_INDEX_APP) then
        handle_home_index(req, send)
    elseif(env.REQUEST_URI ~= nil) and (env.REQUEST_URI == MERCKU_HOME_REDIRECT) then
        handle_home_index_nolocation(req, send)
    else
        handle_home_index(req, send)
    end
end
